{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实现过程描述"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#这里包含densenet网络的变体\n",
    "from __future__ import absolute_import#兼容python3.x和2.x，加入绝对引入\n",
    "from __future__ import division#导入python未来支持的语言特征division(精确除法)\n",
    "from __future__ import print_function #兼容python2.x和3.x的print用法\n",
    "\n",
    "import tensorflow as tf#导入tensorflow模块\n",
    "\n",
    "slim = tf.contrib.slim#非官方，在未加入核心库之前接口可能会被修改，或还需要测试\n",
    "\n",
    "\n",
    "def trunc_normal(stddev): return tf.truncated_normal_initializer(stddev=stddev)#从截断的正态分布中输出随机值，生成的值\n",
    " #服从具有指定平均值和标准偏差的正态分布，如果生成的值大于平均值2个标准偏差的值则丢弃重新选择。\n",
    "\n",
    "\n",
    "def bn_act_conv_drp(current, num_outputs, kernel_size, scope='block'):#H复杂函数\n",
    "    #num_outputs为输出通道数,\n",
    "    current = slim.batch_norm(current, scope=scope + '_bn')#批归一化,在激活函数之前用\n",
    "    current = tf.nn.relu(current)#激活函数\n",
    "    current = slim.conv2d(current, num_outputs, kernel_size, scope=scope + '_conv')#卷积\n",
    "    current = slim.dropout(current, scope=scope + '_dropout')#减小过拟合，在分类器之前用\n",
    "    return current\n",
    "\n",
    "\n",
    "def block(net, layers, growth, scope='block'):#DenseBlock\n",
    "    for idx in range(layers):\n",
    "        #瓶颈层\n",
    "        #1x1的卷积核的使用可以有效降低feature_map的数量，且将特征图的数量统一压缩为4*增长率\n",
    "        bottleneck = bn_act_conv_drp(net, 4 * growth, [1, 1],scope=scope + '_conv1x1' + str(idx))\n",
    "        \n",
    "        #使用Ｈ函数,进行非线性变换，特征图转换为增长率的值的个数\n",
    "        tmp = bn_act_conv_drp(bottleneck, growth, [3, 3],scope=scope + '_conv3x3' + str(idx))\n",
    "        #稠密链接,每次增加growth个特征图在channel的维度上\n",
    "        net = tf.concat(axis=3, values=[net, tmp])\n",
    "    return net#返回net+tmp的值的数量和\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "############################################实现过程描述###################################################\n",
    "                                         ###实现过程描述####\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "def transition(net, num_outputs, scope='transition'):#转换层,1个卷积层和1个2*2的平均池化层\n",
    "    net = bn_act_conv_drp(net, num_outputs, [1, 1], scope=scope + '_conv1x1')\n",
    "    net = slim.avg_pool2d(net, [2, 2], stride=2, scope=scope + '_avgpool')\n",
    "    return net\n",
    "\n",
    "def densenet(images, num_classes=1001, is_training=False,dropout_keep_prob=0.8,scope='densenet'):#实验有3个dense block，每个block 有相同的层\n",
    "   \n",
    "    growth = 24#增长率指定\n",
    "    compression_rate = 0.5#压缩因子指定，因为复现，按照论文中来，压缩因子小于1，加上上面的BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3)，现在此网络为DenseNet-BC\n",
    "\n",
    "    def reduce_dim(input_feature):#减少特征图的数量\n",
    "        return int(int(input_feature.shape[-1]) * compression_rate)\n",
    "\n",
    "    end_points = {}\n",
    "\n",
    "    with tf.variable_scope(scope, 'DenseNet', [images, num_classes]):\n",
    "        with slim.arg_scope(bn_drp_scope(is_training=is_training,keep_prob=dropout_keep_prob)) as ssc:\n",
    "\n",
    "            #因为是DenseNet-BC,所以输出通道数按照２倍的增长率；首先用7*7卷积核，3*3的最大池化\n",
    "            net = slim.conv2d(images, 2*growth, [7, 7], stride=2, padding='same', scope='init_conv')\n",
    "            end_points['init_conv'] = net\n",
    "            net = slim.max_pool2d(net, [3, 3], stride=2, scope='init_conv')\n",
    "            end_points['init_conv'] = net\n",
    "\n",
    "            #增长率小则可能信息提取不出来，太大则导致特征图增加太快，运行速度变慢\n",
    "            net = block(net, 10, growth, scope='block1')\n",
    "            end_points['block1'] = net\n",
    "            \n",
    "            #1转换层\n",
    "            net = transition(net, reduce_dim(net), scope='transition1')\n",
    "            end_points['transition1'] = net\n",
    "            \n",
    "            #第二个dense block\n",
    "            net = block(net, 10, growth, scope='block2')\n",
    "            end_points['block2'] = net\n",
    "            \n",
    "            #2转换层\n",
    "            net = transition(net, reduce_dim(net), scope='transition2')\n",
    "            end_points['transition2'] = net\n",
    "            \n",
    "            #第三个dense block\n",
    "            net = block(net, 10, growth, scope='block3')\n",
    "            end_points['block3'] = net\n",
    "            \n",
    "            #3转换层\n",
    "            net = transition(net, reduce_dim(net), scope='transition3')\n",
    "            end_points['transition3'] = net\n",
    "            \n",
    "            #第四个dense block\n",
    "            net = block(net, 10, growth, scope='block4')\n",
    "            end_points['block4'] = net\n",
    "            \n",
    "            #获取此时卷积核的大小\n",
    "            kernel_size = net.get_shape()[1:3]\n",
    "            \n",
    "            #GAP\n",
    "            net = slim.avg_pool2d(net, kernel_size, padding='VALID', scope='global_pool')\n",
    "            end_points['global_pool'] = net\n",
    "            \n",
    "            #Flatten\n",
    "            net = slim.flatten(net, scope='flatten')\n",
    "            end_points['flatten'] = net\n",
    "            \n",
    "            #fully_connected,logits\n",
    "            logits = slim.fully_connected(net, num_classes,\n",
    "                                  biases_initializer=tf.zeros_initializer(),\n",
    "                                  weights_initializer=trunc_normal(0.1),\n",
    "                                  scope='logits')\n",
    "            end_points['Logits'] = logits\n",
    "            #softmax\n",
    "            end_points['predictions'] = slim.softmax(logits, scope='predictions')\n",
    "\n",
    "    return logits, end_points\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "                                  ###实现过程描述####\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "#############################################实现过程描述######################################################\n",
    "def bn_drp_scope(is_training=True, keep_prob=0.8):\n",
    "    keep_prob = keep_prob if is_training else 1\n",
    "    with slim.arg_scope([slim.batch_norm],scale=True, is_training=is_training, updates_collections=None):\n",
    "        with slim.arg_scope([slim.dropout], is_training=is_training, keep_prob=keep_prob) as bsc:\n",
    "            return bsc\n",
    "\n",
    "\n",
    "def densenet_arg_scope(weight_decay=0.004):\n",
    "    with slim.arg_scope(\n",
    "        [slim.conv2d],\n",
    "        weights_initializer=tf.contrib.layers.variance_scaling_initializer(\n",
    "            factor=2.0, mode='FAN_IN', uniform=False),\n",
    "        activation_fn=None, biases_initializer=None, padding='same',\n",
    "            stride=1) as sc:\n",
    "        return sc\n",
    "\n",
    "\n",
    "densenet.default_image_size = 224\n",
    "     \n",
    "     "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对growth及稠密连接的理解："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "          densenet网络为了最大化的网络之间的信息流动,将每一层与其它层直连（每一层从此前所有层获取输入，并将此层输出传递给此后所有层），\n",
    "    而其重要之处在于降采样，能改变特征图的大小，将网络 分解成多稠密块连接的网络，在其中，这样的话就实现了特征的重复利用，同时层之间的\n",
    "    feature的稠密连接，有效减少梯度消失的问题（在反向传播中，可将底端网络此处梯度直接传回网络始端）。\n",
    "          实验表明一个较小的增长率就可以取得极好的结果。\n",
    "          而growth的最佳值的设定，可把网络在变得窄的效果下，让层学习较少特征，（当然参数也就少了），会加快训练速度。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
